
#include "EventMemoryTable.h"

EventMemoryTable::EventMemoryTable(int parNumMaxSlot, MemoryWrapper* parMemory, float* clusterDelimiters, int numDelimiters, float parLastThreshold):
	numMaxSlot(parNumMaxSlot),
	memory(parMemory),
	lastThreshold(parLastThreshold)
{
	clusterSelector = new ClusterSelector(clusterDelimiters, numDelimiters);
	numUsedSlot = 0;
	keyCounter = 0;
	//lastThreshold = 0.4;

	//timer = new Timer();

}

EventMemoryTable::~EventMemoryTable(){
	delete clusterSelector;
}

void EventMemoryTable::computeInsertion(PubPkt *pkt, int ns, float ws, float lastPercentage){

	cout << "Memory manager: Event table: start message insertion..." << endl;

	//timer->start();

	TimeMs currentTime;
	timestamp_t currentTimestamp = currentTime.getTimeVal();
	bool resettable = true;

	if (ws == 0) ws = 1;
	if (lastPercentage >= lastThreshold) resettable = false;
	if (numUsedSlot == numMaxSlot) {
		cout << "Memory manager: Event table: capacity limit reached: need to remove a message." << endl;
		std::pair<timestamp_t, key_t> candidate;
		boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator pointerToCandidateQueue;
		float lowerPriorityValue = 10000;
		bool foundRemovable = false;
		bool considerNonResettability = true;
		do {
			for (boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it = clustersMap.begin(); it != clustersMap.end(); it++){
				if (it->second.empty() ) continue;
				timestamp_t actualEventConsumptionTimestamp = it->second.top().first;
				key_t key = it->second.top().second;
				boost::unordered_map<key_t, bool>::iterator it3 = keyToResettableMap.find(key);
				if (it3 != keyToResettableMap.end()){
					if (it->second.size()==1 && !it3->second && considerNonResettability) continue;
				}
				boost::unordered_map<key_t, std::pair<int, float> >::iterator it2 = keyToParametersMap.find(key);
				if (it2 != keyToParametersMap.end()){
				//	if (it->second.size()==1 && !actualParameters.resettable) continue;
					int ns = it2->second.first;
					float ws = it2->second.second;
					//cout << "calcolo..." << endl;
					float actualPriorityValue;
					if (actualEventConsumptionTimestamp - currentTimestamp < 0) actualPriorityValue = 0;
					else actualPriorityValue = (ns/ws)*(actualEventConsumptionTimestamp - currentTimestamp);
					//cout << "prio value: " << actualPriorityValue << endl;
					//if (lowerPriorityValue == -1 || actualPriorityValue < lowerPriorityValue){
					if (actualPriorityValue < lowerPriorityValue){
						lowerPriorityValue = actualPriorityValue;
						candidate = it->second.top();
						pointerToCandidateQueue = it;
						foundRemovable = true;
					}
				}
			}
			if (!foundRemovable) { considerNonResettability = false; }
		} while (!foundRemovable);
		pointerToCandidateQueue->second.pop();
		cout << "Memory manager: Event table: remotion from cluster " <<  pointerToCandidateQueue->first << ": key "<< candidate.second << endl;
		memory->remove(candidate);
		numUsedSlot--;
	}

	keyCounter++;
	timestamp_t eventTimestamp = pkt->getTimeStamp().getTimeVal();
	timestamp_t eventConsumptionTimestamp = eventTimestamp + (int long)ws;
	std::string clusterName = clusterSelector->getClusterName(ns/ws);
	cout << "QUI: " << ns/ws << endl;
	boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it3 = clustersMap.find(clusterName);
	if (it3 != clustersMap.end()){
		it3->second.push(make_pair(eventConsumptionTimestamp, keyCounter));
		cout << "Memory manager: Event table: insertion into cluster " << it3->first << ": key " << keyCounter << endl;
	} else {
		std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > queue;
		queue.push(make_pair(eventConsumptionTimestamp, keyCounter));
		clustersMap.insert(make_pair(clusterName, queue));
		cout << "Memory manager: Event table: created cluster " << clusterName << endl;
		cout << "Memory manager: Event table: insertion into cluster " << clusterName << ": key "<< keyCounter << endl;
	}

	//cout << "ns: " << ns << " ws: " << ws << endl;
	keyToParametersMap.insert(make_pair(keyCounter, make_pair(ns, ws)));
	keyToResettableMap.insert(make_pair(keyCounter, resettable));
	memory->insert(pkt, keyCounter);
	numUsedSlot++;

	//timer->stop();
	cout << "Memory manager: Event table: insertion terminated." << endl; // in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
}

void EventMemoryTable::computeExtraction(BackSubPkt *pkt, std::set<PubPkt*> &resultSet){
//	std::set<PubPkt*> resultSet;
	cout << "Memory manager: Event table: start messages extraction..." << endl;
	memory->extract(pkt->getEventType(), pkt->getConstraintList(), pkt->getConstraintsNum(), pkt->getTimeStamp().getTimeVal(), pkt->getWindow(), pkt->getOccurrence(), resultSet);
	cout << "Memory manager: Event table: extraction terminated." << endl;
//	return resultSet;

}



ClusterSelector::ClusterSelector(float* points, int n) {

	std::string namebase = "cluster";

	MINUS_INF = -1000000;
	PLUS_INF = 1000000;
	clustersNumber = n+1;
	clusters = new Cluster[clustersNumber];
	clusters[0].start = MINUS_INF;
	clusters[0].end = points[0];
	clusters[0].name = namebase +'0';
	cout << clusters[0].name << "(" << clusters[0].start << "," << clusters[0].end << ")" << endl;
	for (int i=1; i<n; i++){
		clusters[i].start = points[i-1];
		clusters[i].end = points[i];
		clusters[i].name = namebase + util::intToString(i);
		cout << clusters[i].name << "(" << clusters[i].start << "," << clusters[i].end << ")" << endl;
	}
	clusters[n].start = points[n-1];
	clusters[n].end = PLUS_INF;
	clusters[n].name = namebase + util::intToString(n);
	cout << clusters[n].name << "(" << clusters[n].start << "," << clusters[n].end << ")" << endl;
}


ClusterSelector::~ClusterSelector(){

}


std::string ClusterSelector::getClusterName(float value){
	std::string nameCluster;
	for (int i=0; i<clustersNumber; i++){
		if (((value > clusters[i].start) && (value <= clusters[i].end))){
			nameCluster = clusters[i].name;
			break;
		}
	}
	return nameCluster;
}

